package com.force.provider;

import java.util.Date;

import javax.annotation.PostConstruct;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import br.com.abril.sforce.ProxyChanged;
import br.com.abril.sforce.ProxyProvider;
import br.com.abril.sforce.SalesForceProfile;
import br.com.abril.sforce.SalesForceProfileService;

import com.sforce.async.AsyncApiException;
import com.sforce.async.BulkConnection;
import com.sforce.soap.partner.Connector;
import com.sforce.soap.partner.PartnerConnection;
import com.sforce.soap.partner.fault.LoginFault;
import com.sforce.ws.ConnectionException;
import com.sforce.ws.ConnectorConfig;
import com.topi.event.EventMonitorService;

@Service
public class SalesForceConnectionProvider 
								implements ApplicationListener<ProxyChanged> {

	private static final Logger logger = Logger.getLogger(SalesForceConnectionProvider.class);
	
	@Autowired
	private SalesForceProfileService profileService;

	private ConnectorConfig connectorConfig;

	private PartnerConnection partnerConnection;

	private boolean proxyEnabled;
	
	@Autowired
	private EventMonitorService eventMonitor;
	
	public SalesForceConnectionProvider() {
	}
	
	@PostConstruct
	public void init() {
		loadSoapConfiguration();
		
		if (connectorConfig == null) return;
		
		login();
	
	}
	
	public void loadSoapConfiguration() {

		SalesForceProfile profile = profileService.getCachedProfile();
		
		if (profile == null) return;
		
		connectorConfig = new ConnectorConfig();
		connectorConfig.setAuthEndpoint(profile.getLoginUrl());
		connectorConfig.setUsername(profile.getUsername());
		connectorConfig.setPassword(profile.getPasswordWithToken());
		connectorConfig.setConnectionTimeout(60000);
		connectorConfig.setCompression(true);

		// TODO ADD THIS PROPERTY
		// connectorConfig.setRestEndpoint(getEnviromentProperty("salesforce.connection.rest.endpoit"));
		connectorConfig.setServiceEndpoint(profile.getServiceUrl());

		addProxyConfiguration();

	}

	private void addProxyConfiguration() {
		
		ProxyProvider proxyProvider = profileService.getProxyProvider();

		if (!proxyProvider.isEnabled())
			return;

		proxyEnabled = true;
		connectorConfig.setProxy(proxyProvider.getProxy());
		connectorConfig.setProxyUsername(proxyProvider.getProxyEntry().getUser());
		connectorConfig.setProxyPassword(proxyProvider.getProxyEntry().getPassword());

	}
	

	@Scheduled(fixedRate=500000, initialDelay=200000)
	public void connect ()  {
		logger.info( "................. Connect into Sales Force" );
		try {	
			connectOrThrowsException();
		} catch ( LoginFault e ) {
			Exception ex = new Exception ( e.getExceptionMessage() , e.getCause() );
			setUpProfileCacheWitConnectionFailure(ex);
			eventMonitor.notifySalesForceConnectionFailure(ex);
		} catch ( ConnectionException e ) {
			setUpProfileCacheWitConnectionFailure(e);	
			eventMonitor.notifySalesForceConnectionFailure(e);
		}
		logger.info( "................. Sales Force Connected !!" );
		
	}

	public void connectOrThrowsException () throws ConnectionException {
		
		this.partnerConnection = Connector.newConnection(connectorConfig);
		
		connectorConfig.setSessionId(getSessionId());
		
		setUpProfileCacheWithSucessfullyConnection();

	}

	public void setUpProfileCacheWithSucessfullyConnection () {
			
		SalesForceProfile profile = profileService.getCachedProfile();
		
		profile.setConnected(true);
		profile.setSessionId(connectorConfig.getSessionId());
		profile.setLastConnection(new Date());
		profile.retriveServiceUrlFromReceivedConnection(partnerConnection.getConfig().getServiceEndpoint());
		
	}

	public void setUpProfileCacheWitConnectionFailure (Exception e) {
		
		SalesForceProfile profile = profileService.getCachedProfile();
		
		profile.setConnected(false);
		profile.setSessionId(null);
		profile.setLastConnection(new Date());
		profile.setConnectionStatusInfo( e.getLocalizedMessage() );
		
	}
	
	
	public boolean isLogged() {
		return connectorConfig.getSessionId() != null;
	}
	
	public void login()  {
		
		if ( isLogged() ) return;
		connect();
	}

	public void loginForTransaction() throws ConnectionException {
		
		if ( isLogged() ) return;
		connectOrThrowsException();
	}

	public void clearConnetionInfo() {
		this.partnerConnection = null;
		this.connectorConfig = null;
	}

	public void reloadConfiguration() {
		clearConnetionInfo();
		loadSoapConfiguration();
	}
	
	public PartnerConnection getPartnerConnection() throws ConnectionException {
		if (partnerConnection == null)
			loginForTransaction();
		return partnerConnection;
	}
	
	public String getSessionId() {
		return partnerConnection.getSessionHeader().getSessionId();
	}
	
	public BulkConnection getBulkConnection() throws ConnectionException
												   , AsyncApiException {
		login();
		return new BulkConnection ( connectorConfig ); 
	}
	
	
	public String getServiceUrl() {
		if (profileService.getOne() == null) return "";
		return profileService.getOne().getServiceUrl();
	}
	
	public String getServerUrl() {
		if (profileService.getCachedProfile() == null) return "";
		return profileService.getCachedProfile().getServerUrl();
	}

	public boolean isProxyEnabled() {
		return proxyEnabled;
	}
	
	public ProxyProvider getProxyProvider() {
		return profileService.getProxyProvider();
	}

	@Override
	public void onApplicationEvent(ProxyChanged event) {
		init();
	}

}
